/**
 * 
 */
package fr.gfi.agraf.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import fr.gfi.agraf.exception.AgrafException;
import fr.gfi.agraf.persistence.dao.IActiviteDAO;
import fr.gfi.agraf.persistence.dao.IPerimetreDAO;
import fr.gfi.agraf.persistence.dao.ITacheDAO;
import fr.gfi.agraf.persistence.dao.IUtilisateurDAO;
import fr.gfi.agraf.persistence.entity.Activite;
import fr.gfi.agraf.persistence.entity.Perimetre;
import fr.gfi.agraf.persistence.entity.Saisie;
import fr.gfi.agraf.persistence.entity.Tache;
import fr.gfi.agraf.persistence.entity.Utilisateur;
import fr.gfi.agraf.service.ITdbService;
import fr.gfi.agraf.service.IUtilisateurService;
import fr.gfi.agraf.service.enumeration.EtatEnum;
import fr.gfi.agraf.service.mapper.ActiviteMapper;
import fr.gfi.agraf.service.mapper.PerimetreMapper;
import fr.gfi.agraf.service.mapper.TacheMapper;
import fr.gfi.agraf.service.mapper.UtilisateurMapper;
import fr.gfi.agraf.vue.bean.ActiviteDTO;
import fr.gfi.agraf.vue.bean.PerimetreDTO;
import fr.gfi.agraf.vue.bean.TacheDTO;
import fr.gfi.agraf.vue.bean.TdBFront;
import fr.gfi.agraf.vue.bean.TdbFrontEquipe;
import fr.gfi.agraf.vue.bean.UtilisateurDTO;

/**
 * @author Fw
 * 
 */
@Service("TdbService")
@Transactional(propagation = Propagation.REQUIRED)
public class TdbService implements ITdbService {

	private static final String AUCUN_UTILISATEUR = "Aucun utilisateur";

	@Autowired
	private IUtilisateurDAO utilisateurDAO;

	@Autowired
	private ITacheDAO tacheDAO;

	@Autowired
	private IUtilisateurService utilisateurService;

	@Autowired
	private IPerimetreDAO perimetreDAO;

	@Autowired
	private IActiviteDAO activiteDAO;
	/**
	 * Logger
	 */
	private final static Logger LOGGER = Logger.getLogger(TdbService.class);

	/*
	 * (non-Javadoc)
	 * 
	 * @see fr.gfi.agraf.service.ITdbService#getStatsForTdb(java.lang.Long,
	 * java.lang.Long)
	 */
	public TdBFront getStatsForTdb(final Long idUtilisateur, final Long idUtilisateurConnecte) {
		// Je créé le front servant d'output
		final TdBFront front = new TdBFront();

		final Utilisateur userRecup = utilisateurDAO.read(idUtilisateur);
		// Si l'id n'existe pas, je renvoie l'utilisateur connecté
		if (userRecup != null) {
			front.setUtilisateurChoisi(UtilisateurMapper.entityToDTO(userRecup));
		} else {
			front.setUtilisateurChoisi(UtilisateurMapper.entityToDTO(utilisateurDAO
					.read(idUtilisateurConnecte)));
		}

		setTauxProductiviteAndLibelleResumeGeneral(front.getUtilisateurChoisi().getId(), front);
		setMapsActivitesEnCours(front.getUtilisateurChoisi().getId(), front);

		try {
			front.setListeAllUtilisateurs(utilisateurService.getAllUtilisateurActif());
		} catch (final AgrafException e) {
			front.setListeAllUtilisateurs(new ArrayList<UtilisateurDTO>());
			LOGGER.debug(AUCUN_UTILISATEUR, e);
		}

		return front;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see fr.gfi.agraf.service.ITdbService#getStatsForTdbEquipe()
	 */
	public TdbFrontEquipe getStatsForTdbEquipe() {
		final TdbFrontEquipe front = new TdbFrontEquipe();

		front.setListePerimetre(getListePerimetreToTdbEquipe());

		return front;
	}

	/**
	 * @return
	 */
	private List<PerimetreDTO> getListePerimetreToTdbEquipe() {
		final List<PerimetreDTO> listeOutput = PerimetreMapper.entityToDTOList(perimetreDAO.listerTous());

		// Il s'agit d'un DTO particulier, je dois ajouter les activités
		// "à la main"
		for (final PerimetreDTO perimetreDTO : listeOutput) {
			final List<ActiviteDTO> listeActivite = ActiviteMapper.getListeDTO(
					activiteDAO.getListeActivitesByPerimetreAndUtilisateur(perimetreDTO.getId(), null), null);

			// Je gere les activité en cours et attribuée
			final List<ActiviteDTO> activiteAttribueeEnCours = new ArrayList<ActiviteDTO>();
			for (final ActiviteDTO activiteDTO : listeActivite) {
				final List<UtilisateurDTO> listeUserEnCoursDev = new ArrayList<UtilisateurDTO>();
				boolean isFound = false;
				for (final TacheDTO tache : activiteDTO.getTacheList()) {
					if (!tache.getEtat().getId().equals(EtatEnum.TERMINE.getId())
							&& tache.getUtilisateur() != null && !isFound) {
						isFound = true;
						activiteAttribueeEnCours.add(activiteDTO);
					}
					boolean userExist = false;
					for (final UtilisateurDTO user : listeUserEnCoursDev) {
						if (tache.getUtilisateur() != null
								&& user.getId().equals(tache.getUtilisateur().getId())) {
							userExist = true;
						}
					}
					if (!userExist && tache.getUtilisateur() != null) {
						listeUserEnCoursDev.add(tache.getUtilisateur());
					}
				}
				activiteDTO.setListeUtilisateurEnCoursDev(listeUserEnCoursDev);
			}
			perimetreDTO.setListeActiviteEnCours(activiteAttribueeEnCours);
			perimetreDTO.setListeActivite(listeActivite);
		}

		return listeOutput;
	}

	/**
	 * @param front
	 * @param listeTachesTerminees
	 */
	private void setMapPerimetre(final TdBFront front, final List<Tache> listeTachesTerminees) {
		final Map<PerimetreDTO, Map<ActiviteDTO, List<TacheDTO>>> mapPerimetreTachesTerminees = new TreeMap<PerimetreDTO, Map<ActiviteDTO, List<TacheDTO>>>();
		final Map<Perimetre, Map<Activite, List<Tache>>> mapPerimetreTachesTermineesEntity = new HashMap<Perimetre, Map<Activite, List<Tache>>>();

		for (final Tache tacheTerminee : listeTachesTerminees) {
			final Perimetre perimetreEnCours = tacheTerminee.getActivite().getPerimetre();
			// Si la map ne contient pas de clé correspondant à ce périmetre...
			if (!mapPerimetreTachesTermineesEntity.containsKey(perimetreEnCours)) {
				final Map<Activite, List<Tache>> mapActiviteTerminees = new HashMap<Activite, List<Tache>>();
				final Activite activiteEnCours = tacheTerminee.getActivite();
				mapActiviteTerminees.put(activiteEnCours, new ArrayList<Tache>());
				mapActiviteTerminees.get(activiteEnCours).add(tacheTerminee);
				mapPerimetreTachesTermineesEntity.put(perimetreEnCours, mapActiviteTerminees);
			} else {
				final Activite activiteEnCours = tacheTerminee.getActivite();
				// Si l'activité n'est pas encore connue de la map des
				// périmètres...

				if (!mapPerimetreTachesTermineesEntity.get(perimetreEnCours).containsKey(activiteEnCours)) {
					mapPerimetreTachesTermineesEntity.get(perimetreEnCours).put(activiteEnCours,
							new ArrayList<Tache>());
				}
				mapPerimetreTachesTermineesEntity.get(perimetreEnCours).get(activiteEnCours)
						.add(tacheTerminee);
			}
		}

		// Je converti le tout
		for (final Entry<Perimetre, Map<Activite, List<Tache>>> entry : mapPerimetreTachesTermineesEntity
				.entrySet()) {
			final Perimetre cle = entry.getKey();
			final Map<Activite, List<Tache>> valeur = entry.getValue();

			final PerimetreDTO perimetreOutput = PerimetreMapper.entityToDto(cle);
			final Map<ActiviteDTO, List<TacheDTO>> mapActiviteOutput = new TreeMap<ActiviteDTO, List<TacheDTO>>();
			mapPerimetreTachesTerminees.put(perimetreOutput, mapActiviteOutput);

			for (final Entry<Activite, List<Tache>> entryActivite : valeur.entrySet()) {
				final ActiviteDTO activiteOutput = ActiviteMapper
						.getDtoByEntity(entryActivite.getKey(), null);
				final List<TacheDTO> listeTacheOutput = TacheMapper.entityToDTOList(entryActivite.getValue(),
						null);
				mapPerimetreTachesTerminees.get(perimetreOutput).put(activiteOutput, listeTacheOutput);
			}

		}

		front.setMapPerimetreTachesTerminees(mapPerimetreTachesTerminees);
	}

	/**
	 * Renvoie une map organisée par activité avec les taches a l'interieur
	 * 
	 * @param listeTache
	 * @return
	 */
	private void setMapsActivitesEnCours(final Long idUtilisateur, final TdBFront front) {
		final Map<ActiviteDTO, List<TacheDTO>> mapOutput = new TreeMap<ActiviteDTO, List<TacheDTO>>();
		final Map<Activite, List<Tache>> mapOutputEntity = new HashMap<Activite, List<Tache>>();

		final List<Tache> listeTache = tacheDAO.getTachesActiveByUtilisateurId(idUtilisateur);

		for (final Tache tache : listeTache) {
			final Activite activiteCourante = tache.getActivite();
			if (!mapOutputEntity.containsKey(activiteCourante)) {
				mapOutputEntity.put(activiteCourante, new ArrayList<Tache>());
			}
			mapOutputEntity.get(activiteCourante).add(tache);
		}

		// Je tri et converti
		for (final Entry<Activite, List<Tache>> entry : mapOutputEntity.entrySet()) {
			final Activite cle = entry.getKey();
			final List<Tache> valeur = entry.getValue();

			final List<TacheDTO> listeDto = TacheMapper.entityToDTOList(valeur, null);
			mapOutput.put(ActiviteMapper.getDtoByEntity(cle, null), listeDto);
		}

		front.setMapActiviteEnCours(mapOutput);
	}

	/**
	 * Renvoi le taux de rentabilité en pourcentage sur tous les devs terminés
	 * 
	 * @return
	 */
	private void setTauxProductiviteAndLibelleResumeGeneral(final Long idUtilisateur, final TdBFront front) {
		double sommeConsoSaisies = 0;
		double sommeChargeInit = 0;
		final List<Tache> listeTacheTerminee = tacheDAO.getListeTacheTermineeByUtilisateur(idUtilisateur);
		for (final Tache tache : listeTacheTerminee) {
			double sommeConsoSaisiesTampon = 0;
			for (final Saisie saisie : tache.getListeSaisies()) {
				sommeConsoSaisiesTampon += saisie.getConsomme();
			}
			sommeConsoSaisies += sommeConsoSaisiesTampon;
			sommeChargeInit += tache.getChargeInit();
		}

		final StringBuilder sb = new StringBuilder();
		sb.append("Vous avez terminé ");
		sb.append(listeTacheTerminee.size());
		sb.append(" taches. Au total, vous avez consommé ");
		sb.append((int) sommeConsoSaisies);
		sb.append(" j/h sur ");
		sb.append((int) sommeChargeInit);
		sb.append(" mis à disposition.");

		front.setLibelleResumeGeneral(sb.toString());
		front.setTauxProductivite((int) (sommeChargeInit / sommeConsoSaisies * 100));
		setMapPerimetre(front, listeTacheTerminee);
	}

}
